package pers.chao.cacheable.infrastructure.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import pers.chao.cacheable.infrastructure.properties.RedisCacheProperties;
import pers.chao.cacheable.infrastructure.utils.RedisOp;

import java.time.Duration;

/**
 * Description:
 *
 * @author W.Chao
 * @date 2021/3/28 16:52
 **/
@Configuration
@EnableConfigurationProperties({RedisCacheProperties.class})
public class RedisOperatorConfig {
	@Bean(name = "redisConnectionFactory")
	public RedisConnectionFactory redisConnectionFactory(RedisCacheProperties properties) {
		GenericObjectPoolConfig<Object> poolConfig = new GenericObjectPoolConfig<>();
		poolConfig.setMaxIdle(properties.getPoolMaxIdle());
		poolConfig.setMinIdle(properties.getPoolMinIdle());
		poolConfig.setMaxTotal(properties.getPoolMaxActive());
		poolConfig.setMaxWaitMillis(Duration.ofMillis(properties.getTimeout()).toMillis());

		LettucePoolingClientConfiguration lettucePoolingClientConfig = LettucePoolingClientConfiguration.builder()
				.poolConfig(poolConfig)
				.commandTimeout(Duration.ofSeconds(10))
				.build();

		RedisStandaloneConfiguration redisConfig = new RedisStandaloneConfiguration();
		redisConfig.setHostName(properties.getHost());
		redisConfig.setPort(properties.getPort());
		redisConfig.setDatabase(properties.getDatabase());
		redisConfig.setPassword(properties.getPassword());

		LettuceConnectionFactory lettuceConnectionFactory = new LettuceConnectionFactory(redisConfig, lettucePoolingClientConfig);
		lettuceConnectionFactory.afterPropertiesSet();

		return lettuceConnectionFactory;
	}

	@Bean("redisTemplate")
	@ConditionalOnBean({RedisConnectionFactory.class})
	public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
		RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
		redisTemplate.setConnectionFactory(redisConnectionFactory);
		setSerializer(redisTemplate);
		redisTemplate.afterPropertiesSet();
		return redisTemplate;
	}

	/**
	 * 配置序列化方式
	 *
	 * @param redisTemplate
	 */
	@SuppressWarnings("all")
	private void setSerializer(RedisTemplate<String, Object> redisTemplate) {
		Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
		ObjectMapper om = new ObjectMapper();
		om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
		om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
		jackson2JsonRedisSerializer.setObjectMapper(om);
		StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
		// key采用string序列化
		redisTemplate.setKeySerializer(stringRedisSerializer);
		// key的hash采用string序列化
		redisTemplate.setHashKeySerializer(stringRedisSerializer);
		// value采用jackson序列化
		redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
		// value的hash采用jackson序列化
		redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
	}

	/**
	 * redis操作类
	 *
	 * @param redisTemplate
	 * @return
	 */
	@Bean("redisOp")
	@ConditionalOnBean({RedisTemplate.class})
	public RedisOp redisOp(RedisTemplate<String, Object> redisTemplate) {
		return new RedisOp(redisTemplate);
	}
}
